use crate::client::http::build_client;
use crate::types::{CffexIndex, EndMonth, FinanceQuote, FinanceSymbol, Result, SseUnderlying, SseUnderlyingQuote,
    SseRiskIndicator, SseCurrentDayContract, SzseCurrentDayContract, SseDailyStats, SzseDailyStats};
use crate::utils::parse::{parse_f64, parse_i64};
use serde_json::Value;

/// Fetch SSE underlying snapshot
pub async fn option_finance_sse_underlying(symbol: SseUnderlying) -> Result<SseUnderlyingQuote> {
    let url = match symbol {
        SseUnderlying::HuaXia50 => "http://yunhq.sse.com.cn:32041/v1/sh1/list/self/510050",
        SseUnderlying::HuaTai300 => "http://yunhq.sse.com.cn:32041/v1/sh1/list/self/510300",
        SseUnderlying::NanFang500 => "http://yunhq.sse.com.cn:32041/v1/sh1/list/self/510500",
        SseUnderlying::KeChuang50 => "http://yunhq.sse.com.cn:32041/v1/sh1/list/self/588000",
        SseUnderlying::KeChuang50Yfd => "http://yunhq.sse.com.cn:32041/v1/sh1/list/self/588080",
    };

    let client = build_client()?;
    let resp = client
        .get(url)
        .query(&[("select", "select: code,name,last,change,chg_rate,amp_rate,volume,amount,prev_close")])
        .send()
        .await?;
    
    let json: Value = resp.json().await?;
    let list = json["list"].as_array().ok_or_else(|| anyhow::anyhow!("No list in response"))?;
    
    if list.is_empty() {
        return Err(anyhow::anyhow!("Empty list"));
    }
    
    let row = &list[0];
    let date = json["date"].as_str().unwrap_or("");
    let time = json["time"].as_str().unwrap_or("");
    
    Ok(SseUnderlyingQuote {
        code: row[0].as_str().unwrap_or("").to_string(),
        name: row[1].as_str().unwrap_or("").to_string(),
        last: row[2].as_str().and_then(parse_f64),
        change: row[3].as_str().and_then(parse_f64),
        change_pct: row[4].as_str().and_then(parse_f64),
        volume_lots: row[6].as_str().and_then(parse_f64),
        amount_10k: row[7].as_str().and_then(parse_f64),
        update_time: format!("{}{}", date, time),
    })
}

/// Fetch finance board by symbol and end_month
pub async fn option_finance_board(
    symbol: FinanceSymbol,
    end_month: EndMonth,
) -> Result<Vec<FinanceQuote>> {
    // Extract last 2 digits of month
    let month_suffix = if end_month.0.len() >= 2 {
        &end_month.0[end_month.0.len() - 2..]
    } else {
        &end_month.0
    };

    match symbol {
        FinanceSymbol::Sse50Etf => fetch_sse_board("510050", month_suffix).await,
        FinanceSymbol::Sse300Etf => fetch_sse_board("510300", month_suffix).await,
        FinanceSymbol::Sse500Etf => fetch_sse_board("510500", month_suffix).await,
        FinanceSymbol::SseKc50Etf => fetch_sse_board("588000", month_suffix).await,
        FinanceSymbol::SseKc50EtfYfd => fetch_sse_board("588080", month_suffix).await,
        FinanceSymbol::Szse300Etf => fetch_szse_board(month_suffix).await,
        FinanceSymbol::CffexIO => fetch_cffex_board("IO", month_suffix).await,
        FinanceSymbol::CffexMO => fetch_cffex_board("MO", month_suffix).await,
        FinanceSymbol::CffexHO => fetch_cffex_board("HO", month_suffix).await,
    }
}

/// Fetch SSE option board
async fn fetch_sse_board(code: &str, month_suffix: &str) -> Result<Vec<FinanceQuote>> {
    let url = format!("http://yunhq.sse.com.cn:32041/v1/sho/list/tstyle/{}_{}", code, month_suffix);
    let client = build_client()?;
    let resp = client
        .get(&url)
        .query(&[("select", "contractid,last,chg_rate,presetpx,exepx")])
        .send()
        .await?;
    
    let json: Value = resp.json().await?;
    let list = json["list"].as_array().ok_or_else(|| anyhow::anyhow!("No list in SSE response"))?;
    let date = json["date"].as_str().unwrap_or("");
    let time = json["time"].as_str().unwrap_or("");
    let datetime = format!("{}{}", date, time);
    
    let mut result = Vec::new();
    for row in list {
        if let Some(arr) = row.as_array() {
            result.push(FinanceQuote {
                date_time: datetime.clone(),
                contract_id: arr.get(0).and_then(|v| v.as_str()).unwrap_or("").to_string(),
                last: arr.get(1).and_then(|v| v.as_str()).and_then(parse_f64),
                change_pct: arr.get(2).and_then(|v| v.as_str()).and_then(parse_f64),
                prev_settle: arr.get(3).and_then(|v| v.as_str()).and_then(parse_f64),
                strike: arr.get(4).and_then(|v| v.as_str()).and_then(parse_f64),
            });
        }
    }
    Ok(result)
}

/// Fetch SZSE option board (paginated)
async fn fetch_szse_board(month_suffix: &str) -> Result<Vec<FinanceQuote>> {
    let url = "http://www.szse.cn/api/report/ShowReport/data";
    let client = build_client()?;
    
    // First request to get page count
    let resp = client
        .get(url)
        .query(&[
            ("SHOWTYPE", "JSON"),
            ("CATALOGID", "ysplbrb"),
            ("TABKEY", "tab1"),
            ("PAGENO", "1"),
            ("random", "0.10642298535346595"),
        ])
        .send()
        .await?;
    
    let json: Value = resp.json().await?;
    let page_count = json[0]["metadata"]["pagecount"]
        .as_str()
        .and_then(|s| s.parse::<usize>().ok())
        .unwrap_or(1);
    
    let mut all_data = Vec::new();
    
    // Fetch all pages
    for page in 1..=page_count {
        let resp = client
            .get(url)
            .query(&[
                ("SHOWTYPE", "JSON"),
                ("CATALOGID", "ysplbrb"),
                ("TABKEY", "tab1"),
                ("PAGENO", &page.to_string()),
                ("random", "0.10642298535346595"),
            ])
            .send()
            .await?;
        
        let json: Value = resp.json().await?;
        if let Some(data) = json[0]["data"].as_array() {
            all_data.extend_from_slice(data);
        }
    }
    
    // Filter by month and convert to FinanceQuote
    let mut result = Vec::new();
    for item in all_data {
        if let Some(obj) = item.as_object() {
            // Parse exercise date to extract month
            if let Some(exercise_date) = obj.get("qxqxr").and_then(|v| v.as_str()) {
                // Format: YYYY-MM-DD, extract MM
                if exercise_date.len() >= 7 {
                    let month = &exercise_date[5..7];
                    if month == month_suffix {
                        result.push(FinanceQuote {
                            date_time: "".to_string(),
                            contract_id: obj.get("hym").and_then(|v| v.as_str()).unwrap_or("").to_string(),
                            last: None,
                            change_pct: None,
                            prev_settle: None,
                            strike: obj.get("qxj").and_then(|v| v.as_str()).and_then(parse_f64),
                        });
                    }
                }
            }
        }
    }
    Ok(result)
}

/// Fetch CFFEX option board from text file
async fn fetch_cffex_board(index: &str, month_suffix: &str) -> Result<Vec<FinanceQuote>> {
    let url = match index {
        "IO" => "http://www.cffex.com.cn/quote_IO.txt",
        "MO" => "http://www.cffex.com.cn/quote_MO.txt",
        "HO" => "http://www.cffex.com.cn/quote_HO.txt",
        _ => return Err(anyhow::anyhow!("Unknown CFFEX index")),
    };
    
    let client = build_client()?;
    let resp = client.get(url).send().await?;
    let text = resp.text().await?;
    
    let mut result = Vec::new();
    for line in text.lines().skip(1) { // Skip header
        let fields: Vec<&str> = line.split(',').collect();
        if fields.is_empty() {
            continue;
        }
        
        // Extract month from instrument name (e.g., "IO2501-C-3600" -> "01")
        let instrument = fields[0];
        if let Some(dash_pos) = instrument.find('-') {
            let prefix = &instrument[..dash_pos];
            if prefix.len() >= 6 {
                let month = &prefix[4..6];
                if month == month_suffix {
                    result.push(FinanceQuote {
                        date_time: "".to_string(),
                        contract_id: instrument.to_string(),
                        last: fields.get(5).and_then(|s| parse_f64(s)),
                        change_pct: None,
                        prev_settle: fields.get(4).and_then(|s| parse_f64(s)),
                        strike: None,
                    });
                }
            }
        }
    }
    Ok(result)
}

/// CFFEX board by index & end_month (alias)
pub async fn cffex_board(index: CffexIndex, end_month: EndMonth) -> Result<Vec<FinanceQuote>> {
    let index_str = match index {
        CffexIndex::IO => "IO",
        CffexIndex::MO => "MO",
        CffexIndex::HO => "HO",
    };
    let month_suffix = if end_month.0.len() >= 2 {
        &end_month.0[end_month.0.len() - 2..]
    } else {
        &end_month.0
    };
    fetch_cffex_board(index_str, month_suffix).await
}

/// 上交所风险指标
/// 目标地址: http://www.sse.com.cn/assortment/options/risk/
pub async fn option_risk_indicator_sse(date: &str) -> Result<Vec<SseRiskIndicator>> {
    let url = "http://query.sse.com.cn/commonQuery.do";
    let client = build_client()?;
    
    let params = [
        ("sqlId", "COMMON_SSE_ZQPZ_QQQYZB_DRQQQYZB_SEARCH"),
        ("TRADE_DATE", date),
        ("pageHelp.pageSize", "5000"),
        ("pageHelp.pageNo", "1"),
        ("pageHelp.beginPage", "1"),
        ("pageHelp.cacheSize", "1"),
        ("pageHelp.endPage", "1"),
    ];
    
    let resp = client
        .get(url)
        .query(&params)
        .header("Referer", "http://www.sse.com.cn/")
        .send()
        .await?;
    
    let json: Value = resp.json().await?;
    let data_list = json["result"]
        .as_array()
        .ok_or_else(|| anyhow::anyhow!("No result in SSE risk indicator response"))?;
    
    let mut result = Vec::new();
    for item in data_list {
        if let Some(obj) = item.as_object() {
            result.push(SseRiskIndicator {
                trade_date: obj.get("TRADE_DATE").and_then(|v| v.as_str()).unwrap_or("").to_string(),
                security_id: obj.get("SECURITY_ID").and_then(|v| v.as_str()).unwrap_or("").to_string(),
                delta: obj.get("DELTA_VALUE").and_then(|v| v.as_f64()),
                gamma: obj.get("GAMMA_VALUE").and_then(|v| v.as_f64()),
                theta: obj.get("THETA_VALUE").and_then(|v| v.as_f64()),
                vega: obj.get("VEGA_VALUE").and_then(|v| v.as_f64()),
                rho: obj.get("RHO_VALUE").and_then(|v| v.as_f64()),
                implied_volatility: obj.get("IMPLC_VOLATLTY").and_then(|v| v.as_f64()),
            });
        }
    }
    
    Ok(result)
}

/// 上交所当日合约
/// 目标地址: https://www.sse.com.cn/assortment/options/disclo/preinfo/
pub async fn option_current_day_sse() -> Result<Vec<SseCurrentDayContract>> {
    let url = "http://query.sse.com.cn/commonQuery.do";
    let client = build_client()?;
    
    let params = [
        ("isPagination", "false"),
        ("expireDate", ""),
        ("securityId", ""),
        ("sqlId", "SSE_ZQPZ_YSP_GGQQZSXT_XXPL_DRHY_SEARCH_L"),
    ];
    
    let resp = client
        .get(url)
        .query(&params)
        .header("Referer", "http://www.sse.com.cn/")
        .send()
        .await?;
    
    let json: Value = resp.json().await?;
    let data_list = json["result"]
        .as_array()
        .ok_or_else(|| anyhow::anyhow!("No result in SSE current day response"))?;
    
    let mut result = Vec::new();
    for item in data_list {
        if let Some(obj) = item.as_object() {
            result.push(SseCurrentDayContract {
                contract_code: obj.get("SECURITY_ID").and_then(|v| v.as_str()).unwrap_or("").to_string(),
                contract_id: obj.get("CONTRACT_ID").and_then(|v| v.as_str()).unwrap_or("").to_string(),
                contract_name: obj.get("CONTRACT_SYMBOL").and_then(|v| v.as_str()).unwrap_or("").to_string(),
                underlying_code: obj.get("SECURITYNAMEBYID").and_then(|v| v.as_str()).unwrap_or("").to_string(),
                underlying_name: obj.get("SECURITYNAMEBYID").and_then(|v| v.as_str()).unwrap_or("").to_string(),
                option_type: obj.get("CALL_OR_PUT").and_then(|v| v.as_str()).unwrap_or("").to_string(),
                strike: obj.get("EXERCISE_PRICE").and_then(|v| v.as_f64()),
                contract_unit: obj.get("CONTRACT_UNIT").and_then(|v| v.as_i64()),
                exercise_date: obj.get("END_DATE").and_then(|v| v.as_str()).unwrap_or("").to_string(),
                delivery_date: obj.get("DELIVERY_DATE").and_then(|v| v.as_str()).unwrap_or("").to_string(),
                expire_date: obj.get("EXPIRE_DATE").and_then(|v| v.as_str()).unwrap_or("").to_string(),
                start_date: obj.get("START_DATE").and_then(|v| v.as_str()).unwrap_or("").to_string(),
            });
        }
    }
    
    Ok(result)
}

/// 深交所当日合约
/// 目标地址: https://www.sse.org.cn/option/quotation/contract/daycontract/index.html
pub async fn option_current_day_szse() -> Result<Vec<SzseCurrentDayContract>> {
    let url = "http://www.szse.cn/api/report/exchange/onepersistenthour/option/contract";
    let client = build_client()?;
    
    let resp = client
        .get(url)
        .header("Referer", "http://www.szse.cn/")
        .send()
        .await?;
    
    let json: Value = resp.json().await?;
    let data_list = json["data"]
        .as_array()
        .ok_or_else(|| anyhow::anyhow!("No data in SZSE current day response"))?;
    
    let mut result = Vec::new();
    for item in data_list {
        if let Some(obj) = item.as_object() {
            result.push(SzseCurrentDayContract {
                seq: obj.get("xh").and_then(|v| v.as_i64()),
                contract_code: obj.get("hym").and_then(|v| v.as_str()).unwrap_or("").to_string(),
                contract_id: obj.get("hydm").and_then(|v| v.as_str()).unwrap_or("").to_string(),
                contract_name: obj.get("hyjc").and_then(|v| v.as_str()).unwrap_or("").to_string(),
                underlying_code: obj.get("bddm").and_then(|v| v.as_str()).unwrap_or("").to_string(),
                underlying_name: obj.get("bdjc").and_then(|v| v.as_str()).unwrap_or("").to_string(),
                option_type: obj.get("qqlx").and_then(|v| v.as_str()).unwrap_or("").to_string(),
                strike: obj.get("qxj").and_then(|v| v.as_str()).and_then(parse_f64),
                contract_unit: obj.get("hydw").and_then(|v| v.as_str()).and_then(parse_i64),
                exercise_date: obj.get("qxqxr").and_then(|v| v.as_str()).unwrap_or("").to_string(),
                trade_date: obj.get("jyrq").and_then(|v| v.as_str()).unwrap_or("").to_string(),
            });
        }
    }
    
    Ok(result)
}

/// 上交所每日统计
/// 目标地址: http://www.sse.com.cn/assortment/options/date/
pub async fn option_daily_stats_sse(date: &str) -> Result<Vec<SseDailyStats>> {
    let url = "http://query.sse.com.cn/commonQuery.do";
    let client = build_client()?;
    
    let params = [
        ("isPagination", "false"),
        ("sqlId", "COMMON_SSE_ZQPZ_YSP_QQ_SJTJ_MRTJ_CX"),
        ("tradeDate", date),
    ];
    
    let resp = client
        .get(url)
        .query(&params)
        .header("Referer", "http://www.sse.com.cn/")
        .send()
        .await?;
    
    let json: Value = resp.json().await?;
    let data_list = json["result"]
        .as_array()
        .ok_or_else(|| anyhow::anyhow!("No result in SSE daily stats response"))?;
    
    let mut result = Vec::new();
    for item in data_list {
        if let Some(obj) = item.as_object() {
            result.push(SseDailyStats {
                underlying_code: obj.get("SECURITY_CODE").and_then(|v| v.as_str()).unwrap_or("").to_string(),
                underlying_name: obj.get("SECURITY_ABBR").and_then(|v| v.as_str()).unwrap_or("").to_string(),
                contract_count: obj.get("CONTRACT_VOLUME").and_then(|v| v.as_i64()),
                total_amount: obj.get("TOTAL_MONEY").and_then(|v| v.as_f64()),
                total_volume: obj.get("TOTAL_VOLUME").and_then(|v| v.as_f64()),
                call_volume: obj.get("CALL_VOLUME").and_then(|v| v.as_f64()),
                put_volume: obj.get("PUT_VOLUME").and_then(|v| v.as_f64()),
                total_open_interest: obj.get("LEAVES_QTY").and_then(|v| v.as_f64()),
                call_open_interest: obj.get("LEAVES_CALL_QTY").and_then(|v| v.as_f64()),
                put_open_interest: obj.get("LEAVES_PUT_QTY").and_then(|v| v.as_f64()),
                trade_date: date.to_string(),
            });
        }
    }
    
    Ok(result)
}

/// 深交所每日统计
/// 目标地址: https://investor.szse.cn/market/option/day/index.html
pub async fn option_daily_stats_szse(date: &str) -> Result<Vec<SzseDailyStats>> {
    let url = "http://www.szse.cn/api/report/exchange/onepersistenthour/option/day";
    let client = build_client()?;
    
    let params = [
        ("date", date),
    ];
    
    let resp = client
        .get(url)
        .query(&params)
        .header("Referer", "http://www.szse.cn/")
        .send()
        .await?;
    
    let json: Value = resp.json().await?;
    let data_list = json["data"]
        .as_array()
        .ok_or_else(|| anyhow::anyhow!("No data in SZSE daily stats response"))?;
    
    let mut result = Vec::new();
    for item in data_list {
        if let Some(obj) = item.as_object() {
            result.push(SzseDailyStats {
                underlying_code: obj.get("bddm").and_then(|v| v.as_str()).unwrap_or("").to_string(),
                underlying_name: obj.get("bdjc").and_then(|v| v.as_str()).unwrap_or("").to_string(),
                total_volume: obj.get("cjl").and_then(|v| v.as_str()).and_then(parse_f64),
                call_volume: obj.get("rgcjl").and_then(|v| v.as_str()).and_then(parse_f64),
                put_volume: obj.get("rpcjl").and_then(|v| v.as_str()).and_then(parse_f64),
                total_amount: obj.get("cje").and_then(|v| v.as_str()).and_then(parse_f64),
                total_open_interest: obj.get("wpcczs").and_then(|v| v.as_str()).and_then(parse_f64),
                call_open_interest: obj.get("wprgccs").and_then(|v| v.as_str()).and_then(parse_f64),
                put_open_interest: obj.get("wprpccs").and_then(|v| v.as_str()).and_then(parse_f64),
                trade_date: date.to_string(),
            });
        }
    }
    
    Ok(result)
}
